package com.llb666.shop.user.application;

import com.alibaba.fastjson.JSON;
import com.llb666.common.redis.service.RedisService;
import com.llb666.shop.user.api.vo.auth.Account;
import com.llb666.shop.user.api.vo.auth.AuthResponse;
import com.llb666.shop.user.api.vo.auth.ResponseCode;
import com.llb666.shop.user.domain.service.JwtService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.UUID;

/**
 * 权限应用层.
 *
 * @author llb666
 */
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class AuthApplication {

    private final JwtService jwtService;
    private final RedisService redisService;

    private static final String USER_TOKEN = "redis_user_token:";
    private static final String USER_REFRESH_TOKEN = "redis_user_refresh_token:";

    /**
     * 生成token.
     *
     * @param userId 用户Id
     * @return token
     */
    public AuthResponse token(String userId) {
        Account account = Account.builder().userId(userId).build();
        String token = jwtService.token(account);
        account.setToken(token);
        account.setRefreshToken(UUID.randomUUID().toString());

        redisService.setCacheObject(USER_TOKEN + userId, JSON.toJSONString(account));
        redisService.setCacheObject(USER_REFRESH_TOKEN + account.getRefreshToken(), userId);

        return AuthResponse.builder()
                .account(account)
                .code(ResponseCode.SUCCESS.getCode())
                .build();
    }

    /**
     * 校验token.
     *
     * @param account 权限账户.
     * @return AuthResponse
     */
    public AuthResponse verify(Account account) {
        Boolean aBoolean = redisService.hasKey(USER_TOKEN + account.getUserId());
        if (aBoolean == null || !aBoolean) {
            return AuthResponse.builder()
                    .code(ResponseCode.INCORRECT_CREDENTIAL.getCode())
                    .build();
        }
        boolean verify = jwtService.verify(account.getToken(), account.getUserId());
        return AuthResponse.builder()
                .code(verify ? ResponseCode.SUCCESS.getCode() : ResponseCode.INCORRECT_CREDENTIAL.getCode())
                .build();
    }

    /**
     * 重新获取token.
     *
     * @param refreshToken 刷新用的 refreshToken
     * @return 新token
     */
    public AuthResponse refresh(String refreshToken) {
        Account account = JSON.parseObject(redisService.getCacheObject(USER_REFRESH_TOKEN + refreshToken), Account.class);
        if (account == null) {
            return AuthResponse.builder().code(ResponseCode.USER_NOT_FOUND.getCode()).build();
        }
        //重新获取token
        String token = jwtService.token(account);
        account.setToken(token);
        account.setRefreshToken(UUID.randomUUID().toString());

        redisService.deleteObject(USER_REFRESH_TOKEN + refreshToken);
        redisService.setCacheObject(USER_REFRESH_TOKEN + account.getRefreshToken(), JSON.toJSONString(account));

        return AuthResponse.builder()
                .account(account)
                .code(ResponseCode.SUCCESS.getCode())
                .build();
    }

    /**
     * 删除token.
     *
     * @param account 权限账户.
     * @return AuthResponse
     */
    public AuthResponse delete(Account account) {
        AuthResponse verify = verify(account);
        AuthResponse response = new AuthResponse();
        // 跳过验证
        if (account.isSkipVerification()) {
            redisService.deleteObject(USER_TOKEN + account.getUserId());
            response.setCode(ResponseCode.SUCCESS.getCode());
            return response;
        }
        // 验证成功
        if (verify.getCode().equals(ResponseCode.SUCCESS.getCode())) {
            redisService.deleteObject(USER_TOKEN + account.getUserId());
            redisService.deleteObject(USER_REFRESH_TOKEN + account.getRefreshToken());
            response.setCode(ResponseCode.SUCCESS.getCode());
        } else {
            // 验证失败
            response.setCode(ResponseCode.USER_NOT_FOUND.getCode());
        }
        return response;
    }
}
